home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Sprite 1984 - 1993
/
Sprite 1984 - 1993.iso
/
src
/
kernel
/
fscache
/
fscacheOps.c
< prev
next >
Wrap
C/C++ Source or Header
|
1992-12-18
|
38KB
|
1,242 lines
/*
* fsCacheOps.c --
*
* Cache routines that are monitored with the lock in the per-file
* cacheInfo structure. This includes high level read and write
* routines, and routines that fiddle with the attributes that
* are cached.
*
*
* Copyright 1987 Regents of the University of California
* All rights reserved.
* Permission to use, copy, modify, and distribute this
* software and its documentation for any purpose and without
* fee is hereby granted, provided that the above copyright
* notice appear in all copies. The University of California
* makes no representations about the suitability of this
* software for any purpose. It is provided "as is" without
* express or implied warranty.
*/
#ifndef lint
static char rcsid[] = "$Header: /cdrom/src/kernel/Cvsroot/kernel/fscache/fscacheOps.c,v 9.21 92/10/26 13:55:23 mgbaker Exp $ SPRITE (Berkeley)";
#endif not lint
#include <sprite.h>
#include <fs.h>
#include <fsutil.h>
#include <fscache.h>
#include <fsconsist.h>
#include <fsNameOps.h>
#include <fsdm.h>
#include <fsStat.h>
#include <fslcl.h>
#include <fscacheBlocks.h>
#include <vm.h>
#include <spriteTime.h>
#include <timer.h>
#include <sys.h>
#include <rpc.h>
#define BLOCK_ALIGNED(offset) (((offset) & ~FS_BLOCK_OFFSET_MASK) == (offset))
/*
* Cache I/O is serialized using a monitor lock on the cache state for
* each file.
*/
#define LOCKPTR (&cacheInfoPtr->lock)
/*
* ----------------------------------------------------------------------------
*
* Fscache_UpdateFile --
*
* This routine updates the cacheInfo for a file based on info returned
* from the server at open time. It checks version numbers and the
* cachability of the file, and will do any required invalidations.
*
* Results:
* True if the file in the cache is different than before. This is
* used to tell VM that the cached segment representing the file
* is no longer valid.
*
* Side effects:
* Updates the version number. May invalidate old blocks.
*
* ----------------------------------------------------------------------------
*
*/
ENTRY Boolean
Fscache_UpdateFile(cacheInfoPtr, openForWriting, version, cacheable, attrPtr)
Fscache_FileInfo *cacheInfoPtr; /* Cache state of file to update. */
Boolean openForWriting; /* TRUE if opening for writing */
int version; /* Version number used to verify
* our cached data blocks. */
Boolean cacheable; /* TRUE if server says we can cache. */
Fscache_Attributes *attrPtr; /* Attributes from server */
{
register Boolean outOfDate;
Boolean changed = FALSE;
LOCK_MONITOR;
/*
* See if we have a good cached copy. Note: if we open for writing
* the returned version number will be one greater than ours
* if no-one else has modified the file since we cached it.
*/
if (openForWriting) {
outOfDate = (cacheInfoPtr->version < version - 1);
} else {
outOfDate = (cacheInfoPtr->version < version);
}
if (version > cacheInfoPtr->version) {
/*
* Update the version of the handle, ie. we just opened for writing
* or had an outOfDate version.
*/
cacheInfoPtr->version = version;
changed = TRUE;
}
if (cacheInfoPtr->flags & FSCACHE_FILE_GONE) {
/*
* Reset the deleted bit. By this time any deletion
* actions will have completed.
*/
cacheInfoPtr->flags &= ~FSCACHE_FILE_GONE;
}
/*
* If we were caching and the file is no longer cacheable, or our copy
* is outOfDate, we need to invalidate our cache. Note that we do
* the cache invalidate with our previous notion of the size.
*/
if (!cacheable || outOfDate) {
fs_Stats.handle.cacheFlushes++;
Fscache_FileInvalidate(cacheInfoPtr, 0, FSCACHE_LAST_BLOCK);
}
if (outOfDate) {
fs_Stats.handle.versionMismatch++;
cacheInfoPtr->attr = *attrPtr;
}
/*
* Propogate the cachability of the handle.
*/
if (cacheable) {
if (cacheInfoPtr->flags & FSCACHE_FILE_NOT_CACHEABLE) {
/*
* The handle wasn't client cacheable before. In this
* case mark the handle as cacheable and update the cached
* attributes since they could have changed
* while we didn't have the file cached.
*/
cacheInfoPtr->attr = *attrPtr;
cacheInfoPtr->flags &= ~FSCACHE_FILE_NOT_CACHEABLE;
}
} else {
cacheInfoPtr->flags |= FSCACHE_FILE_NOT_CACHEABLE;
}
/*
* Update the handle's access time. The access time doesn't get
* pushed out to all clients concurrently read-sharing a file,
* so we grab it here if we are out-of-date.
*/
if (attrPtr->accessTime > cacheInfoPtr->attr.accessTime) {
cacheInfoPtr->attr.accessTime = attrPtr->accessTime;
}
UNLOCK_MONITOR;
return (changed);
}
/*
* ----------------------------------------------------------------------------
*
* Fscache_UpdateAttrFromClient --
*
* This is used on the server to update the attributes of a handle
* that has been cached on a client. Called at close time with
* the attributes the client sends over with the close RPC.
* The client's times are checked against the servers to ensure
* that they only increase and never move backwards.
*
* Results:
* None.
*
* Side effects:
* Updates the access and modify time if they are greater than
* the local attribute. Updates the first and last byte indexes.
*
* ----------------------------------------------------------------------------
*
*/
ENTRY void
Fscache_UpdateAttrFromClient(clientID, cacheInfoPtr, attrPtr)
int clientID; /* Client, for warning msg */
register Fscache_FileInfo *cacheInfoPtr; /* Cache state to update. */
register Fscache_Attributes *attrPtr; /* Attributes from client */
{
LOCK_MONITOR;
if (attrPtr->modifyTime > cacheInfoPtr->attr.modifyTime) {
cacheInfoPtr->attr.modifyTime = attrPtr->modifyTime;
}
if (attrPtr->accessTime > cacheInfoPtr->attr.accessTime) {
cacheInfoPtr->attr.accessTime = attrPtr->accessTime;
}
if (cacheInfoPtr->attr.lastByte > attrPtr->lastByte) {
printf(
"Fscache_UpdateAttrFromClient %d: \"%s\" <%d,%d> short size %d not %d\n",
clientID,
Fsutil_HandleName(cacheInfoPtr->hdrPtr),
cacheInfoPtr->hdrPtr->fileID.major,
cacheInfoPtr->hdrPtr->fileID.minor,
attrPtr->lastByte, cacheInfoPtr->attr.lastByte);
} else {
cacheInfoPtr->attr.lastByte = attrPtr->lastByte;
}
cacheInfoPtr->attr.firstByte = attrPtr->firstByte;
(void)Fsdm_UpdateDescAttr((Fsio_FileIOHandle *)cacheInfoPtr->hdrPtr,
&cacheInfoPtr->attr, -1);
UNLOCK_MONITOR;
}
/*
* ----------------------------------------------------------------------------
*
* Fscache_UpdateDirSize --
*
* This is used on the server to update the size of a directory
* when it grows due to additions. Note that directories don't
* get smaller.
*
* Results:
* None.
*
* Side effects:
* Updates the last byte index.
*
* ----------------------------------------------------------------------------
*
*/
ENTRY void
Fscache_UpdateDirSize(cacheInfoPtr, newLastByte)
register Fscache_FileInfo *cacheInfoPtr; /* Cache state to update. */
register int newLastByte; /* New last byte index */
{
LOCK_MONITOR;
if (newLastByte > cacheInfoPtr->attr.lastByte) {
cacheInfoPtr->attr.lastByte = newLastByte;
}
UNLOCK_MONITOR;
}
/*
* ----------------------------------------------------------------------------
*
* Fscache_UpdateAttrFromCache --
*
* This is used on a client to update the given attributes from
* information it has cached. A client caches the access time,
* modify time, and size of a file. When the server returns attributes,
* this routine is called to complete them from info cached here
* on the client.
*
* Results:
* None.
*
* Side effects:
* Updates the access and modify time of *attrPtr if they are less than
* the cached value. Always updates the first and last byte indexes
* with the cached value.
*
* ----------------------------------------------------------------------------
*
*/
ENTRY void
Fscache_UpdateAttrFromCache(cacheInfoPtr, attrPtr)
register Fscache_FileInfo *cacheInfoPtr; /* Cache state to check. */
register Fs_Attributes *attrPtr; /* Attributes from server */
{
LOCK_MONITOR;
if ((cacheInfoPtr->version == attrPtr->version) &&
(cacheInfoPtr->flags & FSCACHE_FILE_NOT_CACHEABLE) == 0) {
if (cacheInfoPtr->attr.accessTime > attrPtr->accessTime.seconds) {
attrPtr->accessTime.seconds = cacheInfoPtr->attr.accessTime;
}
if (cacheInfoPtr->attr.modifyTime > attrPtr->dataModifyTime.seconds) {
attrPtr->dataModifyTime.seconds = cacheInfoPtr->attr.modifyTime;
}
attrPtr->size = cacheInfoPtr->attr.lastByte + 1;
if (cacheInfoPtr->attr.firstByte > 0) {
attrPtr->size -= cacheInfoPtr->attr.firstByte;
}
}
UNLOCK_MONITOR;
}
/*
* ----------------------------------------------------------------------------
*
* Fscache_GetCachedAttr --
*
* This is used on a server to fill out the attributes returned to
* a client for caching there.
*
* Results:
* None.
*
* Side effects:
* None.
*
* ----------------------------------------------------------------------------
*
*/
ENTRY void
Fscache_GetCachedAttr(cacheInfoPtr, versionPtr, attrPtr)
register Fscache_FileInfo *cacheInfoPtr; /* Cache state to update. */
register int *versionPtr; /* Version number of file */
register Fscache_Attributes *attrPtr; /* New attributes from server */
{
LOCK_MONITOR;
*versionPtr = cacheInfoPtr->version;
*attrPtr = cacheInfoPtr->attr;
UNLOCK_MONITOR;
}
/*
* ----------------------------------------------------------------------------
*
* Fscache_UpdateCachedAttr --
*
* This is called during an Fs_SetAttributes call to update attributes
* that are cached in a handle.
*
* Results:
* None.
*
* Side effects:
* Updates the times, permissions, file type, and ownership info,
* depending on the flags argument.
*
* ----------------------------------------------------------------------------
*
*/
ENTRY void
Fscache_UpdateCachedAttr(cacheInfoPtr, attrPtr, flags)
register Fscache_FileInfo *cacheInfoPtr; /* Cache state to update. */
register Fs_Attributes *attrPtr; /* New attributes */
register int flags; /* What attrs to update */
{
LOCK_MONITOR;
if (flags & FS_SET_TIMES) {
cacheInfoPtr->attr.accessTime = attrPtr->accessTime.seconds;
cacheInfoPtr->attr.modifyTime = attrPtr->dataModifyTime.seconds;
cacheInfoPtr->attr.createTime = attrPtr->createTime.seconds;
}
if (flags & FS_SET_MODE) {
cacheInfoPtr->attr.permissions = attrPtr->permissions;
}
if (flags & FS_SET_OWNER) {
if (attrPtr->uid >= 0) {
cacheInfoPtr->attr.uid = attrPtr->uid;
}
if (attrPtr->gid >= 0) {
cacheInfoPtr->attr.gid = attrPtr->gid;
}
}
if ((flags & FS_SET_FILE_TYPE) &&
attrPtr->userType != FS_USER_TYPE_UNDEFINED) {
cacheInfoPtr->attr.userType = attrPtr->userType;
}
UNLOCK_MONITOR;
}
/*
* ----------------------------------------------------------------------------
*
* Fscache_CheckVersion --
*
* This is used during recovery on a server to see if the client
* has an ok version of the file.
*
* Results:
* FS_VERSION_MISMATCH if the client's version number is out-of-date.
*
* Side effects:
* None, except for a print statement.
*
* ----------------------------------------------------------------------------
*
*/
ENTRY ReturnStatus
Fscache_CheckVersion(cacheInfoPtr, version, clientID)
register Fscache_FileInfo *cacheInfoPtr; /* Cache state to check. */
int version;
int clientID;
{
ReturnStatus status = SUCCESS;
LOCK_MONITOR;
if (version != cacheInfoPtr->version) {
printf(
"Version mismatch: clt %d, srv %d, file \"%s\" <%d,%d>, from client %d\n",
version, cacheInfoPtr->version, Fsutil_HandleName(cacheInfoPtr->hdrPtr),
cacheInfoPtr->hdrPtr->fileID.major,
cacheInfoPtr->hdrPtr->fileID.minor, clientID);
status = FS_VERSION_MISMATCH;
}
UNLOCK_MONITOR;
return(status);
}
/*
* ----------------------------------------------------------------------------
*
* Fscache_OkToScavenge --
*
* This is called at handle scavenge time to see if it is ok to
* scavenge the handle. This calls a routine in fsBlockCache.c
* which gets the global cache monitor lock because the blocksInCache
* attribute and FS_FILE_ON_DIRTY list flags is modified under that lock.
*
* Results:
* TRUE if there is no information in the cache for the file.
*
* Side effects:
* None.
*
* ----------------------------------------------------------------------------
*
*/
ENTRY Boolean
Fscache_OkToScavenge(cacheInfoPtr)
register Fscache_FileInfo *cacheInfoPtr; /* Cache state to check. */
{
register Boolean ok;
LOCK_MONITOR;
ok = FscacheBlockOkToScavenge(cacheInfoPtr);
UNLOCK_MONITOR;
return(ok);
}
/*
* ----------------------------------------------------------------------------
*
* Fscache_OkToScavengeExceptDirty --
*
* This is called at handle reopen time to see if it is necessary to
* reopen the handle. This calls a routine in fsBlockCache.c
* which gets the global cache monitor lock because the blocksInCache
* attribute and FS_FILE_ON_DIRTY list flags is modified under that lock.
*
* Results:
* TRUE if there are no references to or dirty blocks for the file.
*
* Side effects:
* None.
*
* ----------------------------------------------------------------------------
*
*/
ENTRY Boolean
Fscache_OkToScavengeExceptDirty(cacheInfoPtr)
register Fscache_FileInfo *cacheInfoPtr; /* Cache state to check. */
{
register Boolean ok;
LOCK_MONITOR;
ok = FscacheBlockOkToScavengeExceptDirty(cacheInfoPtr);
UNLOCK_MONITOR;
return(ok);
}
/*
* ----------------------------------------------------------------------------
*
* Fscache_Consist --
*
* This is called from ProcessConsist to take a cache consistency
* action in response to a callback from the file server.
*
* Results:
* The return status from the block cache operations, plus the
* values of the attributes we have cached here on the client.
*
* Side effects:
* Will writeback and/or invalidate the cache according to the
* request by the server.
*
* ----------------------------------------------------------------------------
*
*/
ENTRY ReturnStatus
Fscache_Consist(cacheInfoPtr, flags, cachedAttrPtr)
register Fscache_FileInfo *cacheInfoPtr;
int flags;
Fscache_Attributes *cachedAttrPtr;
{
ReturnStatus status;
int firstBlock;
int numSkipped;
int mig;
LOCK_MONITOR;
if (cacheInfoPtr->attr.firstByte == -1) {
firstBlock = 0;
} else {
firstBlock = cacheInfoPtr->attr.firstByte / FS_BLOCK_SIZE;
}
status = SUCCESS;
mig = (flags & FSCONSIST_MIGRATION) ? FSCACHE_WB_MIGRATION : 0;
switch (flags & ~(FSCONSIST_MIGRATION)) {
case FSCONSIST_WRITE_BACK_BLOCKS:
status = Fscache_FileWriteBack(cacheInfoPtr, firstBlock,
FSCACHE_LAST_BLOCK, FSCACHE_FILE_WB_WAIT | mig,
&numSkipped);
break;
case FSCONSIST_CANT_CACHE_NAMED_PIPE:
case FSCONSIST_INVALIDATE_BLOCKS:
Fscache_FileInvalidate(cacheInfoPtr, firstBlock,
FSCACHE_LAST_BLOCK);
cacheInfoPtr->flags |= FSCACHE_FILE_NOT_CACHEABLE;
break;
case FSCONSIST_INVALIDATE_BLOCKS | FSCONSIST_WRITE_BACK_BLOCKS:
status = Fscache_FileWriteBack(cacheInfoPtr, firstBlock,
FSCACHE_LAST_BLOCK,
FSCACHE_WRITE_BACK_AND_INVALIDATE |
FSCACHE_FILE_WB_WAIT | mig,
&numSkipped);
cacheInfoPtr->flags |= FSCACHE_FILE_NOT_CACHEABLE;
break;
case FSCONSIST_DELETE_FILE:
cacheInfoPtr->flags |= FSCACHE_FILE_GONE;
Fscache_FileInvalidate(cacheInfoPtr, firstBlock, FSCACHE_LAST_BLOCK);
cacheInfoPtr->flags |= FSCACHE_FILE_NOT_CACHEABLE;
break;
case FSCONSIST_WRITE_BACK_ATTRS:
break;
default:
printf("Fscache_Consist: Bad consistency action %x\n", flags);
status = FS_INVALID_ARG;
break;
}
*cachedAttrPtr = cacheInfoPtr->attr;
UNLOCK_MONITOR;
return(status);
}
/*
*----------------------------------------------------------------------
*
* Fscache_Read --
*
* Read from the block cache.
*
* Results:
* SUCCESS unless there was an address error or I/O error.
*
* Side effects:
* Fill in the buffer and the cache with data from the stream.
* The cache state for the file is locked to serialize I/O.
*
*----------------------------------------------------------------------
*/
ReturnStatus
Fscache_Read(cacheInfoPtr, flags, buffer, offset, lenPtr, remoteWaitPtr)
register Fscache_FileInfo *cacheInfoPtr; /* Cache state for file. */
int flags; /* FS_USER | etc */
register Address buffer; /* Buffer to fill with file data */
int offset; /* Byte offset */
int *lenPtr; /* In/Out byte count */
Sync_RemoteWaiter *remoteWaitPtr; /* Process info for remote waiting */
{
register int size; /* Amount left to read */
register int blockNum; /* Current block being read */
register int toRead; /* Amount to read each iteration */
ReturnStatus status = SUCCESS; /* Return from I/O operations */
int firstBlock; /* First block of the read */
int lastBlock; /* Last block of the read */
Boolean found; /* For fetching blocks from cache */
Fscache_Block *blockPtr; /* For fetching blocks from cache */
int dontBlock; /* FSCACHE_DONT_BLOCK */
/*
* Serialiaze access to the cache for this file.
*/
LOCK_MONITOR;
if (cacheInfoPtr->flags & FSCACHE_FILE_NOT_CACHEABLE) {
status = FS_NOT_CACHEABLE;
goto exit;
}
/*
* Determine the offset at which to read.
*/
if (offset > cacheInfoPtr->attr.lastByte) {
*lenPtr = 0;
status = SUCCESS;
goto exit;
}
if ((remoteWaitPtr != (Sync_RemoteWaiter *)NIL) &&
(remoteWaitPtr->pid != (Proc_PID)NIL)) {
dontBlock = FSCACHE_DONT_BLOCK;
} else {
dontBlock = 0;
}
/*
* Fetch blocks one at a time.
*/
size = *lenPtr;
firstBlock = (unsigned int) offset / FS_BLOCK_SIZE;
lastBlock = (unsigned int) (offset + size - 1) / FS_BLOCK_SIZE;
for (blockNum = firstBlock;
blockNum <= lastBlock && offset <= cacheInfoPtr->attr.lastByte;
blockNum++, size -= toRead, buffer += toRead, offset += toRead) {
/*
* Initiate read ahead on the next block.
*/
FscacheReadAhead(cacheInfoPtr, blockNum + 1);
/*
* Determine the number of bytes to transfer out of the cache block
* in this go around.
*/
toRead = size;
if ((unsigned int) (offset + size - 1) / FS_BLOCK_SIZE > blockNum) {
toRead = (blockNum + 1) * FS_BLOCK_SIZE - offset;
}
if (toRead > cacheInfoPtr->attr.lastByte - offset + 1) {
toRead = cacheInfoPtr->attr.lastByte - offset + 1;
}
/*
* Get the block from the cache. If the block isn't in the cache
* then read data into it.
*/
Fscache_FetchBlock(cacheInfoPtr, blockNum,
FSCACHE_DATA_BLOCK|dontBlock, &blockPtr, &found);
if (blockPtr == (Fscache_Block *)NIL) {
/*
* Cache is full.
*/
status = FS_WOULD_BLOCK;
Fsutil_WaitListInsert(fscacheFullWaitList, remoteWaitPtr);
break;
}
fs_Stats.blockCache.readAccesses++;
if (found) {
if (blockPtr->timeDirtied != 0) {
fs_Stats.blockCache.readHitsOnDirtyBlock++;
} else {
fs_Stats.blockCache.readHitsOnCleanBlock++;
}
if (blockPtr->flags & FSCACHE_READ_AHEAD_BLOCK) {
fs_Stats.blockCache.readAheadHits++;
}
} else {
/*
* We didn't find the block in the cache so we have to read it in.
* Fscache_FetchBlock has set the blockNum and blockAddr (buffer).
* blockSize gets set as a side-effect of the block read routine.
*/
status = (cacheInfoPtr->backendPtr->ioProcs.blockRead)
(cacheInfoPtr->hdrPtr, blockPtr, remoteWaitPtr);
#ifdef lint
status = Fsio_FileBlockRead(cacheInfoPtr->hdrPtr,
blockPtr, remoteWaitPtr);
status = FsrmtFileBlockRead(cacheInfoPtr->hdrPtr,
blockPtr, remoteWaitPtr);
#endif /* lint */
if (status != SUCCESS) {
fs_Stats.blockCache.domainReadFails++;
Fscache_UnlockBlock(blockPtr, (time_t)0, -1, 0,
FSCACHE_DELETE_BLOCK);
break;
}
}
/*
* Copy the bytes out of the cache block.
*/
if (flags & FS_USER) {
if (Vm_CopyOut(toRead,
blockPtr->blockAddr + (offset & FS_BLOCK_OFFSET_MASK),
buffer) != SUCCESS) {
status = SYS_ARG_NOACCESS;
Fscache_UnlockBlock(blockPtr, (time_t)0, -1, 0,
FSCACHE_CLEAR_READ_AHEAD);
break;
}
} else {
bcopy(blockPtr->blockAddr + (offset & FS_BLOCK_OFFSET_MASK),
buffer, toRead);
}
Fscache_UnlockBlock(blockPtr, (time_t)0, -1, 0,
FSCACHE_CLEAR_READ_AHEAD);
}
*lenPtr -= size;
Fs_StatAdd(*lenPtr, fs_Stats.blockCache.bytesRead,
fs_Stats.blockCache.bytesReadOverflow);
exit:
if ((status == SUCCESS) ||
(status == FS_WOULD_BLOCK && (*lenPtr > 0))) {
cacheInfoPtr->attr.accessTime = Fsutil_TimeInSeconds();
}
UNLOCK_MONITOR;
return(status);
}
/*
*----------------------------------------------------------------------
*
* Fscache_Write --
*
* Write to the cache. Called from the file write and named pipe
* write routines.
*
* Results:
* A return status, SUCCESS if successful.
*
* Side effects:
* Data is put into the cache. Blocks may be read if only partial
* blocks are being written. The cache state is locked during the I/O
* to serialize cache access.
*
*----------------------------------------------------------------------
*/
ReturnStatus
Fscache_Write(cacheInfoPtr, flags, buffer, offset, lenPtr, remoteWaitPtr)
register Fscache_FileInfo *cacheInfoPtr; /* Cache state for the file. */
int flags; /* FS_USER | FS_APPEND |
* FS_SERVER_WRITE_THRU */
register Address buffer; /* Buffer to write from */
int offset; /* Byte offset */
int *lenPtr; /* In/Out byte count */
Sync_RemoteWaiter *remoteWaitPtr; /* Process info for remote waiting */
{
register int size; /* The current size left to write */
register int toWrite; /* Amount to write each iteration */
int toAlloc; /* Amount of disk space to allocate.*/
register int blockNum; /* The current block being written */
Fscache_Block *blockPtr; /* From fetching cached blocks */
int blockSize; /* The number of bytes in the current
* block. */
ReturnStatus status = SUCCESS; /* Return from I/O operations */
int firstBlock; /* The first block of the write */
int lastBlock; /* The last block to write */
Boolean found; /* From fetching cached blocks */
int oldOffset; /* Initial value of the offset */
int lastFileBlock; /* Last block in the file */
int blockAddr; /* For allocating blocks */
Boolean newBlock; /* A brand new block was allocated. */
time_t modTime; /* File modify time. */
Boolean dontBlock; /* TRUE if lower levels shouldn't block
* because we can block up higher */
/*
* Serialize access to the cache for this file.
*/
LOCK_MONITOR;
if (cacheInfoPtr->flags & FSCACHE_FILE_NOT_CACHEABLE) {
/*
* Not cached. The flag is checked here under monitor lock.
*/
status = FS_NOT_CACHEABLE;
goto exit;
}
if (cacheInfoPtr->flags & FSCACHE_FILE_GONE) {
/*
* A delayed write is arriving as the file is being deleted.
*/
printf( "Write to deleted file #%d\n",
cacheInfoPtr->hdrPtr->fileID.minor);
status = FS_FILE_REMOVED;
*lenPtr = 0;
goto exit;
}
/*
* Determine where to start writing.
*/
if (flags & FS_APPEND) {
offset = cacheInfoPtr->attr.lastByte + 1;
}
oldOffset = offset;
size = *lenPtr;
*lenPtr = 0;
/*
* Determine the range of blocks to write and where the current last block
* in the file is.
*/
firstBlock = (unsigned int) offset / FS_BLOCK_SIZE;
lastBlock = (unsigned int) (offset + size - 1) / FS_BLOCK_SIZE;
if (cacheInfoPtr->attr.lastByte == -1) {
lastFileBlock = -1;
} else {
lastFileBlock = ((unsigned int) cacheInfoPtr->attr.lastByte) /
FS_BLOCK_SIZE;
}
/*
* If we have a handle on a process then our caller can block it.
* Otherwise we have to block at a low level in FetchBlock.
*/
if ((remoteWaitPtr != (Sync_RemoteWaiter *)NIL) &&
(remoteWaitPtr->pid != (Proc_PID)NIL)) {
dontBlock = FSCACHE_DONT_BLOCK;
} else {
dontBlock = 0;
}
/*
* Put the data into the cache a block at a time.
*/
for (blockNum = firstBlock;
blockNum <= lastBlock;
blockNum++, size -= toWrite, buffer += toWrite, offset += toWrite) {
/*
* Determine the number of bytes to write into the cache block
* this go around and the number of bytes to allocate on disk.
*/
if ((unsigned int) (offset + size - 1) / FS_BLOCK_SIZE > blockNum) {
/*
* Writing will go into the next block. In this case fill up the
* rest of the current block and allocate space for the rest of
* the current block.
*/
toWrite = FS_BLOCK_SIZE - (offset & FS_BLOCK_OFFSET_MASK);
toAlloc = toWrite;
} else {
/*
* The write ends in this block.
* There are two cases for disk allocation:
* 1) We are writing before the last byte in the file. In this
* case allocate up to the last byte or the end of the
* current block whichever comes first.
* 2) We are after the last byte in the file. In this case just
* allocate space for the newly written bytes.
*/
toWrite = size;
if (offset + toWrite - 1 < cacheInfoPtr->attr.lastByte) {
if (cacheInfoPtr->attr.lastByte >=
(blockNum + 1) * FS_BLOCK_SIZE) {
toAlloc = FS_BLOCK_SIZE - (offset & FS_BLOCK_OFFSET_MASK);
} else {
toAlloc = cacheInfoPtr->attr.lastByte - offset + 1;
}
} else {
toAlloc = toWrite;
}
}
/*
* Allocate space behind the cache block.
*/
status = (cacheInfoPtr->backendPtr->ioProcs.allocate)(cacheInfoPtr->hdrPtr,
offset, toAlloc, dontBlock, &blockAddr, &newBlock);
#ifdef lint
status = Fsdm_BlockAllocate(cacheInfoPtr->hdrPtr,
offset, toAlloc, dontBlock, &blockAddr, &newBlock);
status = FsrmtFileBlockAllocate(cacheInfoPtr->hdrPtr,
offset, toAlloc, dontBlock, &blockAddr, &newBlock);
#endif /* lint */
if (blockAddr == FSDM_NIL_INDEX) {
if (status == SUCCESS) {
if ((cacheInfoPtr->flags & FSCACHE_ALLOC_FAILED) == 0) {
printf("%s: allocator returned SUCCESS but no block.\n",
"Fscache_Write"); /* DEBUG */
}
status = FS_NO_DISK_SPACE;
}
blockPtr = (Fscache_Block *)NIL;
} else {
fs_Stats.blockCache.writeAccesses++;
Fscache_FetchBlock(cacheInfoPtr, blockNum,
(int)(FSCACHE_IO_IN_PROGRESS | FSCACHE_DATA_BLOCK | dontBlock),
&blockPtr, &found);
if (blockPtr == (Fscache_Block *)NIL) {
status = FS_WOULD_BLOCK;
}
}
if (blockPtr == (Fscache_Block *)NIL) {
if (status == FS_NO_DISK_SPACE) {
/*
* Limit the number of "Alloc failed" messages to 1 per file.
* Servers with hard-copy consoles like this feature.
*/
if ((cacheInfoPtr->flags & FSCACHE_ALLOC_FAILED) == 0) {
cacheInfoPtr->flags |= FSCACHE_ALLOC_FAILED;
printf("Fscache_Write: Alloc failed <%d,%d> \"%s\" %s\n",
cacheInfoPtr->hdrPtr->fileID.major,
cacheInfoPtr->hdrPtr->fileID.major,
Fsutil_HandleName(cacheInfoPtr->hdrPtr),
"DISK FULL");
}
}
break;
} else {
cacheInfoPtr->flags &= ~FSCACHE_ALLOC_FAILED;
}
if (toWrite == FS_BLOCK_SIZE) {
if (found) {
fs_Stats.blockCache.overWrites++;
}
} else {
/*
* Check if are writing into the middle of the file and are not
* overwriting the block. If so have to read the block in if
* it is not in the cache.
*/
if (blockNum <= lastFileBlock && !newBlock) {
if (found) {
fs_Stats.blockCache.partialWriteHits++;
if (blockPtr->flags & FSCACHE_READ_AHEAD_BLOCK) {
fs_Stats.blockCache.readAheadHits++;
}
} else {
fs_Stats.blockCache.partialWriteMisses++;
status = (cacheInfoPtr->backendPtr->ioProcs.blockRead)
(cacheInfoPtr->hdrPtr, blockPtr, remoteWaitPtr);
#ifdef lint
status = Fsio_FileBlockRead(cacheInfoPtr->hdrPtr,
blockPtr, remoteWaitPtr);
status = FsrmtFileBlockRead(cacheInfoPtr->hdrPtr,
blockPtr, remoteWaitPtr);
#endif /* lint */
if (status != SUCCESS) {
fs_Stats.blockCache.domainReadFails++;
Fscache_UnlockBlock(blockPtr, (time_t)0, -1, 0,
FSCACHE_DELETE_BLOCK);
break;
}
}
} else {
/*
* We are writing to the end of the file or the block
* that we are writing to is brand new. In this case zero
* fill the block if it isn't in the cache yet.
*/
if (!found) {
fs_Stats.blockCache.writeZeroFills2++;
bzero(blockPtr->blockAddr, FS_BLOCK_SIZE);
}
}
}
/*
* Copy the bytes into the block.
*/
if (flags & FS_USER) {
if (Vm_CopyIn(toWrite, buffer, blockPtr->blockAddr +
(offset & FS_BLOCK_OFFSET_MASK)) != SUCCESS) {
status = SYS_ARG_NOACCESS;
Fscache_UnlockBlock(blockPtr, (time_t)0, -1, 0,
FSCACHE_DELETE_BLOCK);
break;
}
} else {
bcopy(buffer, blockPtr->blockAddr + (offset & FS_BLOCK_OFFSET_MASK), toWrite);
}
/*
* If the block is write-thru then write the data through to the
* server and then check the block back in as clean.
* THIS IS A GORY HACK that limits the use of FS_SERVER_WRITE_THRU
* to the client.
*/
if (flags & FS_SERVER_WRITE_THRU) {
Fs_Stream dummyStream;
Fs_IOParam io;
Fs_IOReply reply;
io.buffer = blockPtr->blockAddr + (offset & FS_BLOCK_OFFSET_MASK);
io.length = toWrite;
io.offset = offset;
io.flags = flags | FS_CLIENT_CACHE_WRITE;
io.flags &= ~(FS_USER | FS_SERVER_WRITE_THRU);
dummyStream.hdr.fileID.type = -1;
dummyStream.ioHandlePtr = cacheInfoPtr->hdrPtr;
status = Fsrmt_Write(&dummyStream, &io,
(Sync_RemoteWaiter *)NIL, &reply);
if (status != SUCCESS) {
Fscache_UnlockBlock(blockPtr, (time_t)0, -1, 0,
FSCACHE_DELETE_BLOCK);
break;
}
modTime = 0;
} else {
modTime = Fsutil_TimeInSeconds();
}
/*
* Return the block to the cache. At this time the block
* size is changed (by UnlockBlock).
*/
if (offset + toWrite - 1 > cacheInfoPtr->attr.lastByte) {
cacheInfoPtr->attr.lastByte = offset + toWrite - 1;
}
blockSize = cacheInfoPtr->attr.lastByte + 1 -
(blockNum * FS_BLOCK_SIZE);
if (blockSize > FS_BLOCK_SIZE) {
blockSize = FS_BLOCK_SIZE;
}
Fscache_UnlockBlock(blockPtr, modTime, blockAddr,
blockSize, FSCACHE_CLEAR_READ_AHEAD |
((flags&FS_WRITE_TO_DISK) ?
FSCACHE_WRITE_TO_DISK : 0));
}
*lenPtr = offset - oldOffset;
Fs_StatAdd(offset - oldOffset, fs_Stats.blockCache.bytesWritten,
fs_Stats.blockCache.bytesWrittenOverflow);
/*
* Update the firstByte so that Fs_Read knows there is data.
* This is support for named pipes, which care about firstByte.
*/
if (cacheInfoPtr->attr.firstByte == -1 && *lenPtr > 0) {
cacheInfoPtr->attr.firstByte = 0;
}
if (!(flags & FS_CLIENT_CACHE_WRITE) && *lenPtr > 0) {
/*
* Update the modify time unless this write is a flush back from a
* user cache in which case we already get the correct mod time
* for the file when the client closes.
*/
cacheInfoPtr->attr.modifyTime = Fsutil_TimeInSeconds();
}
exit:
if (status == FS_WOULD_BLOCK) {
Fsutil_FastWaitListInsert(fscacheFullWaitList, remoteWaitPtr);
}
UNLOCK_MONITOR;
return(status);
}
/*
*----------------------------------------------------------------------
*
* Fscache_BlockRead --
*
* Return a pointer to the cache block that contains a
* block of a file. This is used to directly access cache blocks
* and so avoid the cost of a copy. The number of valid bytes
* in the cache block is returned.
*
* Results:
* SUCCESS or error when reading file system block. Upon failure,
* or a zero length read with the allocate flag not set, the cache block
* is not locked down and the caller doesn't have to worry about it;
* NIL is returned in *blockPtrPtr.
* This returns FS_WOULD_BLOCK if blockType includes FSCACHE_DONT_BLOCK
* and the cache is full.
*
* Side effects:
* The cache block is locked down, and its contents are filled in
* from disk if neccessary. The caller has to unlock the block when done,
* if it is non-NIL.
*
*----------------------------------------------------------------------
*/
ReturnStatus
Fscache_BlockRead(cacheInfoPtr, blockNum, blockPtrPtr, numBytesPtr, blockType,
allocate)
register Fscache_FileInfo *cacheInfoPtr; /* File to read block from.
* Should be locked on entry */
int blockNum; /* Block to read. */
register Fscache_Block **blockPtrPtr; /* Where to put pointer to
* block.*/
register int *numBytesPtr; /* Return number of bytes
* read. */
int blockType; /* One of FSCACHE_DATA_BLOCK
* and FSCACHE_DIR_BLOCK.
* | FSCACHE_DONT_BLOCK */
Boolean allocate; /* TRUE => return the cache
* block even though there
* is not data in it. */
{
Boolean found;
ReturnStatus status = SUCCESS;
int offset;
Fscache_Block *blockPtr;
LOCK_MONITOR;
*blockPtrPtr = (Fscache_Block *)NIL;
*numBytesPtr = 0;
if (cacheInfoPtr->flags & FSCACHE_FILE_NOT_CACHEABLE) {
panic( "Fscache_BlockRead, file not cacheable!\n");
status = FS_NOT_CACHEABLE;
goto exit;
}
offset = blockNum * FS_BLOCK_SIZE;
if (offset > cacheInfoPtr->attr.lastByte) {
status = SUCCESS;
goto exit;
}
fs_Stats.blockCache.readAccesses++;
if (blockType & FSCACHE_DIR_BLOCK) {
fs_Stats.blockCache.dirBlockAccesses++;
}
Fscache_FetchBlock(cacheInfoPtr, blockNum, blockType,
blockPtrPtr, &found);
if (*blockPtrPtr == (Fscache_Block *)NIL) {
status = FS_WOULD_BLOCK;
goto exit;
}
blockPtr = *blockPtrPtr;
if (!found) {
/*
* Read in the cache block. If the block isn't full (includes EOF)
* then blockSize is set and the rest of the cache block has
* been zero filled.
*/
status = (cacheInfoPtr->backendPtr->ioProcs.blockRead)
(cacheInfoPtr->hdrPtr, blockPtr, (Sync_RemoteWaiter *) NIL);
#ifdef lint
status = Fsdm_FileBlockRead(cacheInfoPtr->hdrPtr,
blockPtr, (Sync_RemoteWaiter *) NIL);
status = FsrmtFileBlockRead(cacheInfoPtr->hdrPtr,
blockPtr, (Sync_RemoteWaiter *) NIL);
#endif /* lint */
if (status == SUCCESS && blockPtr->blockSize == 0 &&
offset < cacheInfoPtr->attr.lastByte + 1) {
/*
* Due to delayed writes the disk descriptor has no space allocated
* and the file block read routine thinks we've read past eof.
* Actually were in a hole in the file and should return zeros.
* The blockRead routine always zero fills for us.
*/
printf("Fscache_BlockRead: Giving zeros to \"%s\" <%d,%d> block %d amount %d\n",
Fsutil_HandleName(cacheInfoPtr->hdrPtr),
cacheInfoPtr->hdrPtr->fileID.major,
cacheInfoPtr->hdrPtr->fileID.minor,
blockPtr->blockNum,
cacheInfoPtr->attr.lastByte - offset);
blockPtr->blockSize = cacheInfoPtr->attr.lastByte - offset;
if (blockPtr->blockSize > FS_BLOCK_SIZE) {
blockPtr->blockSize = FS_BLOCK_SIZE;
}
}
if ((status != SUCCESS) ||
(blockPtr->blockSize == 0 && !allocate)) {
/*
* We hit a disk error or are really past the end-of-file.
*/
Fscache_UnlockBlock(blockPtr, (time_t)0, -1, 0,
FSCACHE_DELETE_BLOCK);
*blockPtrPtr = (Fscache_Block *)NIL;
}
} else {
if (blockType & FSCACHE_DIR_BLOCK) {
fs_Stats.blockCache.dirBlockHits++;
}
if (blockPtr->flags & FSCACHE_READ_AHEAD_BLOCK) {
fs_Stats.blockCache.readAheadHits++;
}
if (blockPtr->timeDirtied != 0) {
fs_Stats.blockCache.readHitsOnDirtyBlock++;
} else {
fs_Stats.blockCache.readHitsOnCleanBlock++;
}
}
if (status == SUCCESS) {
*numBytesPtr = blockPtr->blockSize;
}
if (blockType & FSCACHE_DIR_BLOCK) {
fs_Stats.blockCache.dirBytesRead += blockPtr->blockSize;
} else {
Fs_StatAdd(blockPtr->blockSize, fs_Stats.blockCache.bytesRead,
fs_Stats.blockCache.bytesReadOverflow);
}
/*
* Read ahead the next block.
*/
FscacheReadAhead(cacheInfoPtr, blockNum + 1);
exit:
UNLOCK_MONITOR;
return(status);
}
/*
*----------------------------------------------------------------------
*
* Fscache_Trunc --
*
* Truncate data out of the cache. This knows how to truncate
* Consuming streams.
*
* Results:
* The return status of backend truncate.
*
* Side effects:
* Data blocks are removed from the cache. The file's first and
* last byte indexes get updated. The modify time of the file
* gets set.
*
*----------------------------------------------------------------------
*/
ReturnStatus
Fscache_Trunc(cacheInfoPtr, length, flags)
Fscache_FileInfo *cacheInfoPtr;
int length;
int flags; /* FSCACHE_TRUNC_DELETE */
{
int firstBlock;
int lastBlock;
ReturnStatus status;
LOCK_MONITOR;
if (flags & FSCACHE_TRUNC_DELETE) {
cacheInfoPtr->flags |= FSCACHE_FILE_GONE;
}
if ((cacheInfoPtr->flags & FS_NOT_CACHEABLE) == 0) {
if (length - 1 < cacheInfoPtr->attr.lastByte) {
/*
* Do file like truncation, leave length bytes at the
* beginning of the file.
*/
if (length == 0) {
firstBlock = 0;
} else {
firstBlock = (length - 1) / FS_BLOCK_SIZE + 1;
}
lastBlock = cacheInfoPtr->attr.lastByte / FS_BLOCK_SIZE;
if (length - 1 < cacheInfoPtr->attr.firstByte) {
cacheInfoPtr->attr.lastByte = -1;
cacheInfoPtr->attr.firstByte = -1;
} else {
cacheInfoPtr->attr.lastByte = length - 1;
}
Fscache_FileInvalidate(cacheInfoPtr, firstBlock, lastBlock);
if (firstBlock > 0) {
Fscache_BlockTrunc(cacheInfoPtr, firstBlock - 1,
length - (firstBlock - 1) * FS_BLOCK_SIZE);
}
}
cacheInfoPtr->attr.modifyTime = Fsutil_TimeInSeconds();
}
status = (cacheInfoPtr->backendPtr->ioProcs.truncate)(cacheInfoPtr->hdrPtr,
length, (flags & FSCACHE_TRUNC_DELETE) != 0);
UNLOCK_MONITOR;
return status;
}